home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / fortune-.tar / fortune- / fortune / util / strfile.c < prev    next >
C/C++ Source or Header  |  1995-10-03  |  13KB  |  538 lines

  1. /*      $NetBSD: strfile.c,v 1.3 1995/03/23 08:28:47 cgd Exp $  */
  2.  
  3. /*-
  4.  * Copyright (c) 1989, 1993
  5.  *    The Regents of the University of California.  All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * Ken Arnold.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  * 3. All advertising materials mentioning features or use of this software
  19.  *    must display the following acknowledgement:
  20.  *    This product includes software developed by the University of
  21.  *    California, Berkeley and its contributors.
  22.  * 4. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38.  
  39. /*
  40.  * Changes, September 1995, to make the damn thing actually sort instead
  41.  * of just pretending.  Amy A. Lewis
  42.  * 
  43.  * And lots more.
  44.  * 
  45.  * Fixed the special cases of %^J% (an empty fortune), no 'separator' at
  46.  * the end of the file, and a trailing newline at the end of the file, all
  47.  * of which produced total ballsup at one point or another.
  48.  * 
  49.  * This included adding a routine to go back and write over the last pointer
  50.  * written or stored, for the case of an empty fortune.
  51.  * 
  52.  * unstr also had to be modified (well, for *lots* of reasons, but this was
  53.  * one) to be certain to put the delimiters in the right places.
  54.  */
  55.  
  56. /*
  57.  * 
  58.  #ifndef lint
  59.  static char copyright[] =
  60.  "@(#) Copyright (c) 1989, 1993\n\
  61.  The Regents of the University of California.  All rights reserved.\n";
  62.  #endif / * not lint * /
  63.  
  64.  #ifndef lint
  65.  #if 0
  66.  static char sccsid[] = "@(#)strfile.c  8.1 (Berkeley) 5/31/93";
  67.  #else
  68.  static char rcsid[] = "$NetBSD: strfile.c,v 1.3 1995/03/23 08:28:47 cgd Exp $";
  69.  #endif
  70.  #endif / * not lint * /
  71.  *
  72.  *I haven't the faintest flipping idea what all that is, so kill the warnings 
  73.  */
  74.  
  75. #include    <stdlib.h>
  76. #include    <unistd.h>
  77. #include    <endian.h>
  78. #include    <netinet/in.h>
  79. #include    <sys/param.h>
  80. #include    <stdio.h>
  81. #include    <ctype.h>
  82. #include    <string.h>
  83. #include    "strfile.h"
  84.  
  85. #ifndef MAXPATHLEN
  86. #define    MAXPATHLEN    1024
  87. #endif /* MAXPATHLEN */
  88.  
  89. /*
  90.  *    This program takes a file composed of strings seperated by
  91.  * lines starting with two consecutive delimiting character (default
  92.  * character is '%') and creates another file which consists of a table
  93.  * describing the file (structure from "strfile.h"), a table of seek
  94.  * pointers to the start of the strings, and the strings, each terminated
  95.  * by a null byte.  Usage:
  96.  *
  97.  *      % strfile [-iorsx] [ -cC ] sourcefile [ datafile ]
  98.  *
  99.  *      c - Change delimiting character from '%' to 'C'
  100.  *      s - Silent.  Give no summary of data processed at the end of
  101.  *          the run.
  102.  *      o - order the strings in alphabetic order
  103.  *      i - if ordering, ignore case 
  104.  *      r - randomize the order of the strings
  105.  *      x - set rotated bit
  106.  *
  107.  *              Ken Arnold      Sept. 7, 1978 --
  108.  *
  109.  *      Added ordering options.
  110.  * 
  111.  * Made ordering options do more than set the bloody flag, September 95 A. Lewis
  112.  * 
  113.  * Always make sure that your loop control variables aren't set to bloody
  114.  * *zero* before distributing the bloody code, all right?
  115.  * 
  116.  */
  117.  
  118. #define    TRUE    1
  119. #define    FALSE    0
  120.  
  121. #define    STORING_PTRS    (Oflag || Rflag)
  122. #define    CHUNKSIZE    512
  123.  
  124. #define    ALWAYS    1
  125. #define    ALLOC(ptr,sz)    if (ALWAYS) { \
  126.             if (ptr == NULL) \
  127.                 ptr = malloc((unsigned int) (CHUNKSIZE * sizeof *ptr)); \
  128.             else if (((sz) + 1) % CHUNKSIZE == 0) \
  129.                 ptr = realloc((void *) ptr, ((unsigned int) ((sz) + CHUNKSIZE) * sizeof *ptr)); \
  130.             if (ptr == NULL) { \
  131.                 fprintf(stderr, "out of space\n"); \
  132.                 exit(1); \
  133.             } \
  134.         }
  135.  
  136. typedef struct
  137. {
  138.     char first;
  139.     off_t pos;
  140. }
  141. STR;
  142.  
  143. char *Infile = NULL,        /* input file name */
  144.   Outfile[MAXPATHLEN] = "",    /* output file name */
  145.   Delimch = '%';        /* delimiting character */
  146.  
  147. int Sflag = FALSE;        /* silent run flag */
  148. int Oflag = FALSE;        /* ordering flag */
  149. int Iflag = FALSE;        /* ignore case flag */
  150. int Rflag = FALSE;        /* randomize order flag */
  151. int Xflag = FALSE;        /* set rotated bit */
  152. long Num_pts = 0;        /* number of pointers/strings */
  153.  
  154. off_t *Seekpts;
  155.  
  156. FILE *Sort_1, *Sort_2;        /* pointers for sorting */
  157.  
  158. STRFILE Tbl;            /* statistics table */
  159.  
  160. STR *Firstch;            /* first chars of each string */
  161.  
  162. void usage(void)
  163. {
  164.     fprintf(stderr,
  165.         "strfile [-iorsx] [-c char] sourcefile [datafile]\n");
  166.     exit(1);
  167. }
  168.  
  169. /*
  170.  *    This routine evaluates arguments from the command line
  171.  */
  172. void getargs(int argc, char **argv)
  173. {
  174.     extern char *optarg;
  175.     extern int optind;
  176.     int ch;
  177.  
  178.     while ((ch = getopt(argc, argv, "c:iorsx")) != EOF)
  179.     switch (ch)
  180.       {
  181.       case 'c':        /* new delimiting char */
  182.           Delimch = *optarg;
  183.           if (!isascii(Delimch))
  184.           {
  185.           printf("bad delimiting character: '\\%o\n'",
  186.              Delimch);
  187.           }
  188.           break;
  189.       case 'i':        /* ignore case in ordering */
  190.           Iflag++;
  191.           break;
  192.       case 'o':        /* order strings */
  193.           Oflag++;
  194.           break;
  195.       case 'r':        /* randomize pointers */
  196.           Rflag++;
  197.           break;
  198.       case 's':        /* silent */
  199.           Sflag++;
  200.           break;
  201.       case 'x':        /* set the rotated bit */
  202.           Xflag++;
  203.           break;
  204.       case '?':
  205.       default:
  206.           usage();
  207.       }
  208.     argv += optind;
  209.  
  210.     if (*argv)
  211.     {
  212.     Infile = *argv;
  213.     if (*++argv)
  214.         (void) strcpy(Outfile, *argv);
  215.     }
  216.     if (!Infile)
  217.     {
  218.     puts("No input file name");
  219.     usage();
  220.     }
  221.     if (*Outfile == '\0')
  222.     {
  223.     strcpy(Outfile, Infile);
  224.     strcat(Outfile, ".dat");
  225.     }
  226. }
  227.  
  228. /*
  229.  * add_offset:
  230.  *      Add an offset to the list, or write it out, as appropriate.
  231.  */
  232. void add_offset(FILE * fp, off_t off)
  233. {
  234.     off_t net;
  235.  
  236.     if (!STORING_PTRS)
  237.     {
  238.     net = htonl(off);
  239.     fwrite(&net, 1, sizeof net, fp);
  240.     }
  241.     else
  242.     {
  243.     ALLOC(Seekpts, Num_pts + 1);
  244.     Seekpts[Num_pts] = off;
  245.     }
  246.     Num_pts++;
  247. }
  248.  
  249. /*
  250.  * fix_last_offset:
  251.  *     Used when we have two separators in a row.
  252.  */
  253. void fix_last_offset(FILE * fp, off_t off)
  254. {
  255.     off_t net;
  256.  
  257.     if (!STORING_PTRS)
  258.     {
  259.     net = htonl(off);
  260.     fseek(fp, -(sizeof net), SEEK_CUR);
  261.     fwrite(&net, 1, sizeof net, fp);
  262.     }
  263.     else
  264.     Seekpts[Num_pts - 1] = off;
  265. }
  266.  
  267. /*
  268.  * cmp_str:
  269.  *      Compare two strings in the file
  270.  */
  271. int cmp_str(const void *v1, const void *v2)
  272. {
  273.     register int c1, c2;
  274.     register int n1, n2;
  275.     register STR *p1, *p2;
  276.  
  277. #define    SET_N(nf,ch)    (nf = (ch == '\n'))
  278. #define    IS_END(ch,nf)    (ch == Delimch && nf)
  279.  
  280.     p1 = (STR *) v1;
  281.     p2 = (STR *) v2;
  282.     c1 = p1->first;
  283.     c2 = p2->first;
  284.     if (c1 != c2)
  285.     return c1 - c2;
  286.  
  287.     fseek(Sort_1, p1->pos, 0);
  288.     fseek(Sort_2, p2->pos, 0);
  289.  
  290.     n1 = FALSE;
  291.     n2 = FALSE;
  292.     while (!isalnum(c1 = getc(Sort_1)) && c1 != '\0')
  293.     SET_N(n1, c1);
  294.     while (!isalnum(c2 = getc(Sort_2)) && c2 != '\0')
  295.     SET_N(n2, c2);
  296.  
  297.     while (!IS_END(c1, n1) && !IS_END(c2, n2))
  298.     {
  299.     if (Iflag)
  300.     {
  301.         if (isupper(c1))
  302.         c1 = tolower(c1);
  303.         if (isupper(c2))
  304.         c2 = tolower(c2);
  305.     }
  306.     if (c1 != c2)
  307.         return c1 - c2;
  308.     SET_N(n1, c1);
  309.     SET_N(n2, c2);
  310.     c1 = getc(Sort_1);
  311.     c2 = getc(Sort_2);
  312.     }
  313.     if (IS_END(c1, n1))
  314.     c1 = 0;
  315.     if (IS_END(c2, n2))
  316.     c2 = 0;
  317.     return c1 - c2;
  318. }
  319.  
  320. /*
  321.  * do_order:
  322.  *      Order the strings alphabetically (possibly ignoring case).
  323.  */
  324. void
  325.   do_order(void)
  326. {
  327.     register long i;
  328.     register off_t *lp;
  329.     register STR *fp;
  330.  
  331.     Sort_1 = fopen(Infile, "r");
  332.     Sort_2 = fopen(Infile, "r");
  333.     qsort((char *) Firstch, (int) Num_pts - 1, sizeof *Firstch, cmp_str);
  334. /*      i = Tbl.str_numstr;
  335.  * Fucking brilliant.  Tbl.str_numstr was initialized to zero, and is still zero
  336.  */
  337.     i = Num_pts - 1;
  338.     lp = Seekpts;
  339.     fp = Firstch;
  340.     while (i--)
  341.     *lp++ = fp++->pos;
  342.     fclose(Sort_1);
  343.     fclose(Sort_2);
  344.     Tbl.str_flags |= STR_ORDERED;
  345. }
  346.  
  347. char *
  348.   unctrl(char c)
  349. {
  350.     static char buf[3];
  351.  
  352.     if (isprint(c))
  353.     {
  354.     buf[0] = c;
  355.     buf[1] = '\0';
  356.     }
  357.     else if (c == 0177)
  358.     {
  359.     buf[0] = '^';
  360.     buf[1] = '?';
  361.     }
  362.     else
  363.     {
  364.     buf[0] = '^';
  365.     buf[1] = c + 'A' - 1;
  366.     }
  367.     return buf;
  368. }
  369.  
  370. /*
  371.  * randomize:
  372.  *      Randomize the order of the string table.  We must be careful
  373.  *      not to randomize across delimiter boundaries.  All
  374.  *      randomization is done within each block.
  375.  */
  376. void randomize(void)
  377. {
  378.     register int cnt, i;
  379.     register off_t tmp;
  380.     register off_t *sp;
  381.     extern time_t time(time_t *);
  382.  
  383.     srandom((int) (time((time_t *) NULL) + getpid()));
  384.  
  385.     Tbl.str_flags |= STR_RANDOM;
  386. /*      cnt = Tbl.str_numstr;
  387.  * See comment above.  Isn't this stuff distributed worldwide?  How embarrassing!
  388.  */
  389.     cnt = Num_pts;
  390.  
  391.     /*
  392.      * move things around randomly
  393.      */
  394.  
  395.     for (sp = Seekpts; cnt > 0; cnt--, sp++)
  396.     {
  397.     i = random() % cnt;
  398.     tmp = sp[0];
  399.     sp[0] = sp[i];
  400.     sp[i] = tmp;
  401.     }
  402. }
  403.  
  404. /*
  405.  * main:
  406.  *      Drive the sucker.  There are two main modes -- either we store
  407.  *      the seek pointers, if the table is to be sorted or randomized,
  408.  *      or we write the pointer directly to the file, if we are to stay
  409.  *      in file order.  If the former, we allocate and re-allocate in
  410.  *      CHUNKSIZE blocks; if the latter, we just write each pointer,
  411.  *      and then seek back to the beginning to write in the table.
  412.  */
  413. int main(int ac, char **av)
  414. {
  415.     register unsigned char *sp;
  416.     register FILE *inf, *outf;
  417.     register off_t last_off, length, pos, *p;
  418.     register int first, cnt;
  419.     register char *nsp;
  420.     register STR *fp;
  421.     static char string[257];
  422.  
  423.     getargs(ac, av);        /* evalute arguments */
  424.     if ((inf = fopen(Infile, "r")) == NULL)
  425.     {
  426.     perror(Infile);
  427.     exit(1);
  428.     }
  429.  
  430.     if ((outf = fopen(Outfile, "w")) == NULL)
  431.     {
  432.     perror(Outfile);
  433.     exit(1);
  434.     }
  435.     if (!STORING_PTRS)
  436.     (void) fseek(outf, sizeof Tbl, 0);
  437.  
  438.     /*
  439.      * Write the strings onto the file
  440.      */
  441.  
  442.     Tbl.str_longlen = 0;
  443.     Tbl.str_shortlen = (unsigned int) 0xffffffff;
  444.     Tbl.str_delim = Delimch;
  445.     Tbl.str_version = VERSION;
  446.     first = Oflag;
  447.     add_offset(outf, ftell(inf));
  448.     last_off = 0;
  449.     do
  450.     {
  451.     sp = fgets(string, 256, inf);
  452.     if (sp == NULL || STR_ENDSTRING(sp, Tbl))
  453.     {
  454.         pos = ftell(inf);
  455.         length = pos - last_off - (sp ? strlen(sp) : 0);
  456.         if (!length)
  457.         /* Here's where we go back and fix things, if the
  458.          * 'fortune' just read was the null string.
  459.          * We had to make the assignment of last_off slightly
  460.          * redundant to achieve this.
  461.          */
  462.         {
  463.         if (pos - last_off == 2)
  464.             fix_last_offset(outf, pos);
  465.         last_off = pos;
  466.         continue;
  467.         }
  468.         last_off = pos;
  469.         add_offset(outf, pos);
  470.         if (Tbl.str_longlen < length)
  471.         Tbl.str_longlen = length;
  472.         if (Tbl.str_shortlen > length)
  473.         Tbl.str_shortlen = length;
  474.         first = Oflag;
  475.     }
  476.     else if (first)
  477.     {
  478.         for (nsp = sp; !isalnum(*nsp); nsp++)
  479.         continue;
  480.         ALLOC(Firstch, Num_pts);
  481.         fp = &Firstch[Num_pts - 1];
  482.         if (Iflag && isupper(*nsp))
  483.         fp->first = tolower(*nsp);
  484.         else
  485.         fp->first = *nsp;
  486.         fp->pos = Seekpts[Num_pts - 1];
  487.         first = FALSE;
  488.     }
  489.     }
  490.     while (sp != NULL);
  491.  
  492.     /*
  493.      * write the tables in
  494.      */
  495.  
  496.     fclose(inf);
  497.  
  498.     if (Oflag)
  499.     do_order();
  500.     else if (Rflag)
  501.     randomize();
  502.  
  503.     if (Xflag)
  504.     Tbl.str_flags |= STR_ROTATED;
  505.  
  506.     if (!Sflag)
  507.     {
  508.     printf("\"%s\" created\n", Outfile);
  509.     if (Num_pts == 2)
  510.         puts("There was 1 string");
  511.     else
  512.         printf("There were %ld strings\n", Num_pts - 1);
  513.     printf("Longest string: %lu byte%s\n", Tbl.str_longlen,
  514.            Tbl.str_longlen == 1 ? "" : "s");
  515.     printf("Shortest string: %lu byte%s\n", Tbl.str_shortlen,
  516.            Tbl.str_shortlen == 1 ? "" : "s");
  517.     }
  518.  
  519.     fseek(outf, (off_t) 0, 0);
  520.     Tbl.str_version = htonl(Tbl.str_version);
  521.     Tbl.str_numstr = htonl(Num_pts - 1);
  522.     /* Look, Ma!  After using the variable three times, let's store
  523.      * something in it!
  524.      */
  525.     Tbl.str_longlen = htonl(Tbl.str_longlen);
  526.     Tbl.str_shortlen = htonl(Tbl.str_shortlen);
  527.     Tbl.str_flags = htonl(Tbl.str_flags);
  528.     fwrite((char *) &Tbl, sizeof Tbl, 1, outf);
  529.     if (STORING_PTRS)
  530.     {
  531.     for (p = Seekpts, cnt = Num_pts; cnt--; ++p)
  532.         *p = htonl(*p);
  533.     fwrite((char *) Seekpts, sizeof *Seekpts, (int) Num_pts, outf);
  534.     }
  535.     fclose(outf);
  536.     exit(0);
  537. }
  538.